/*
 * @(#)EncryptUtils.java 1.0 2014-3-20下午8:28:29
 *
 * 德邦证券股份有限公司
 * Copyright (c) 2012-2014 Tebon, Inc. All rights reserved.
 */
package com.saturn.config.server.util;

import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 
 * <dl>
 *    <dt><b>Title:</b></dt>
 *    <dd>
 *    	none
 *    </dd>
 *    <dt><b>Description:</b></dt>
 *    <dd>
 *    	<p>none
 *    </dd>
 * </dl>
 *
 * @author tanks
 * @version 1.0, 2014-7-4
 * @since sirius-commons
 *
 */
public class EncryptUtils {
	
	private static final Logger logger = LoggerFactory.getLogger(EncryptUtils.class);
	
	public static final JceProvider DEFAULT_PROVIDER = JceProvider.SUN_PROVIDER;    
	public static final JceAlgorithm DEFAULT_DES_ALGORITHM = JceAlgorithm.DES;	
	public static final String DEFAULT_IV = "0102030405060708"; 

	public static final String GBK_CHARSET = "GBK";
	public static final String GB2312_CHARSET = "gb2312";
	public static final String UTF8_CHARSET = "utf-8";
	public static final String ISO_CHARSET = "iso-8859-1";
	public static final String DEFAULT_CHARSET = UTF8_CHARSET;	
		
	private EncryptUtils(){}
	
	/**
	 * 3DES秘钥
	 * @return
	 */
	public static String[] genDotNet3DesKeyt(){
		String[] keys = new String[2];
		try{
			KeyGenerator keyGenerator = KeyGenerator.getInstance("DESede");
			keyGenerator.init(new SecureRandom());
			byte[] key = keyGenerator.generateKey().getEncoded();
			keys[0] = EncodeUtils.byteToHex(key);
			keys[1] = EncodeUtils.encodeBase64(key);
		}catch(Exception ex){
			logger.error("error",ex);
		}
		return keys;
	}

	/**
	 * 获得deskey
	 * @param key
	 * @param algorithm
	 * @return
	 */
	public static final SecretKey generateDesKey(String key){
		JceAlgorithm algorithm = getAlgorithmByKey(key);
		SecretKey secretKey = null;
		try {
			KeySpec des = null;
			if (JceAlgorithm.DESede.equals(algorithm)) {
				des = new DESedeKeySpec(key.getBytes());
			} else {
				des = new DESKeySpec(key.getBytes());
			}
			SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm.name());
			secretKey = keyFactory.generateSecret(des);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return secretKey;
	}
	
	/**
	 *  加载秘钥，生产秘钥对象,用于与大网进行3DES加解密用
	 * @param key
	 * @return
	 */
	public static final SecretKey generateDesKeyFor3DESDotNet(String key){
		return EncryptUtils.generateDesKeyFor3DESDotNet(EncodeUtils.hexToByte(key));
	}
	
	/**
	 * 加载秘钥，生产秘钥对象,用于与大网进行3DES加解密用
	 * @param key
	 * @return
	 */
	public static final SecretKey generateDesKeyFor3DESDotNet(byte[] key){
		SecretKey secretKey = null;
		try {
			KeySpec des = new DESedeKeySpec(key);
			SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("desede");
			secretKey = keyFactory.generateSecret(des);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return secretKey;
	}
	
	/**
	 * 根据key的长度获得对应des算法
	 * @param key
	 * @return
	 */
	private static JceAlgorithm getAlgorithmByKey(String key){
		JceAlgorithm algorithm = null;
		if (key.length() == 16) {
			algorithm = JceAlgorithm.DES;
		} else if (key.length() == 48) {
			algorithm = JceAlgorithm.DESede;
		}
		return algorithm;
	}
		
	/**
	 * des加解密
	 * @param provider			- 加解密类库提供商 缺省为 SunJCE
	 * @param key				- 加解密key
	 * @param byteSrc			- 明文或密文数组
	 * @param transformation	- 传输模式 缺省为 DES/ECB/NoPadding
	 * @param iv				- 初始化向量，缺省为空
	 * @return
	 * @throws Exception
	 */
	public static byte[] desCipher(JceProvider provider,SecretKey key,byte[] byteSrc,JceTransformation transformation,byte[] iv,int mode) throws Exception {
		if (transformation == null){
			transformation = JceTransformation.DES_ECB_NOPADDING;
		}
		Cipher des = Cipher.getInstance(transformation.toString(), provider.toString());
		if (iv==null||iv.length==0) {
			des.init(mode, key);
		} else {
			IvParameterSpec ivSpec = new IvParameterSpec(iv);
			des.init(mode, key, ivSpec);
		}
		return des.doFinal(byteSrc);
	}

	/**
	 * des解密
	 * @param provider			- 加解密类库提供商 缺省为 SunJCE
	 * @param key				- 解密key
	 * @param src				- 密文
	 * @param transformation	- 传输模式,为空缺省为 DES/ECB/NoPadding
	 * @param iv				- 初始化向量，缺省为空
	 * @return
	 * @throws Exception
	 */
	public static String desDecrypt(JceProvider provider,SecretKey key, String src,JceTransformation transformation,String iv) throws Exception {
		return new String(desCipher(provider,key,EncodeUtils.hexToByte(src),transformation,EncodeUtils.hexToByte(iv),Cipher.DECRYPT_MODE));
	}
	
	/**
	 * des加密
	 * @param provider			- 加解密类库提供商 缺省为 SunJCE
	 * @param key				- 加密key
	 * @param src				- plain text
	 * @param transformation	- 传输模式，为空缺省为 DES/ECB/NoPadding
	 * @param iv				- 初始化向量，缺省为空
	 * @return
	 * @throws Exception
	 */
	public static String desEncrypt(JceProvider provider,SecretKey key, String src,JceTransformation transformation,String iv) throws Exception {
		return EncodeUtils.byteToHex(desCipher(provider,key,src.getBytes(EncryptUtils.DEFAULT_CHARSET),transformation,EncodeUtils.hexToByte(iv),Cipher.ENCRYPT_MODE));
	}
	
	/**
	 * des加密，类库提供商为 SunJCE，传输模式为 DES/ECB/PKCS5Padding， 初始化向量为空
	 * @param src			- plain text
	 * @param key			- 加密key
	 * @return
	 * @throws Exception
	 */
	public static String desEncrypt(String src,String key) throws Exception {
		return desEncrypt(DEFAULT_PROVIDER,generateDesKey(key),src,JceTransformation.DES_ECB,null);
	}

	/**
	 * des解密，类库提供商为 SunJCE，传输模式为 DES/ECB/NoPadding， 初始化向量为空
	 * @param src
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String desDecrypt(String src,String key) throws Exception {
		return desDecrypt(DEFAULT_PROVIDER,generateDesKey(key),src,null,null);
	}
	
	/**
	 * 使用3des加密算法，对明文进行加密，与大网进行交互
	 * @param src
	 * @param key 16进制字符串
	 * @param iv  16进制字符串
	 * @return
	 * @throws Exception
	 */
	public static String desEncryptWithDotNet(String src,String key,String iv) throws Exception {
		return desEncrypt(DEFAULT_PROVIDER,generateDesKeyFor3DESDotNet(key),src,JceTransformation.DESEDE_CBC,iv);
	}

	public static String desDecryptWithDotNet(String src,String key,String iv) throws Exception {
		return desDecrypt(DEFAULT_PROVIDER,generateDesKeyFor3DESDotNet(key),src,JceTransformation.DESEDE_CBC,iv);
	}
	
	/**
	 * 3des加密，类库提供商为 SunJCE，传输模式为 DES/ECB/PKCS5Padding， 
	 * @param src - plain text
	 * @param key - 加密key
	 * @param vi -初始化向量为空
	 * @return
	 * @throws Exception
	 */
	public static String tripleDesEncrypt(String src,String key,String vi) throws Exception{
		return desEncrypt(DEFAULT_PROVIDER,generateDesKey(key),src,JceTransformation.DESEDE_CBC,vi);
	}
	/**
	 * 3des解密，类库提供商为 SunJCE，传输模式为 DES/ECB/NoPadding，
	 * @param src
	 * @param key
	 * @param vi -- 初始化向量为空
	 * @return
	 * @throws Exception
	 */
	public static String tripleDesDecrypt(String src,String key,String vi) throws Exception{
		return desDecrypt(DEFAULT_PROVIDER, generateDesKey(key), src, JceTransformation.DESEDE_CBC, vi);
	}
	
	/**
	 * MD5 摘要计算(byte[]).
	 *
	 * @param src byte[]
	 * @throws Exception
	 * @return byte[] 16 bit digest
	 */
	public static final byte[] md5Digest(byte[] src) throws Exception {
		if (ArrayUtils.isEmpty(src)) return ArrayUtils.EMPTY_BYTE_ARRAY;
		MessageDigest alg = MessageDigest.getInstance(JceAlgorithm.MD5.toString()); // MD5 is 16 bit coremessage digest
		return alg.digest(src);
	}

	/**
	 * 32位MD5 摘要计算(String).
	 *
	 * @param src String
	 * @throws Exception
	 * @return String
	 */
	public static final String md5Digest(String src) throws Exception {
		if (StringUtils.isBlank(src)) return StringUtils.EMPTY;
		char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',  'e', 'f'};  
		byte tmp[] = md5Digest(src.getBytes(EncryptUtils.DEFAULT_CHARSET));
		char str[] = new char[16 * 2]; 
		int k = 0;                                
		for (int i = 0; i < 16; i++) {        
			byte byte0 = tmp[i];                
			str[k++] = hexDigits[byte0 >>> 4 & 0xf];  
			str[k++] = hexDigits[byte0 & 0xf];           
		} 
		return new String(str);
	}
	

	/**
	 * SHA-1 摘要计算(byte[]).
	 * 
	 * @param src byte[]
	 * @return Exception
	 * @throws Exception
	 */
	public static final byte[] sha1Digest(byte[] src) throws Exception {
		if (ArrayUtils.isEmpty(src)) return ArrayUtils.EMPTY_BYTE_ARRAY;
		MessageDigest alg = MessageDigest.getInstance(JceAlgorithm.SHA1.toString());
		alg.update(src);
		return alg.digest();	
	}

	/**
	 * SHA-1 摘要计算(String).
	 * 
	 * @param src String
	 * @return Exception
	 * @throws Exception
	 */
	public static final String sha1Digest(String src) throws Exception {
		if (StringUtils.isBlank(src)) return StringUtils.EMPTY;
		return new String(EncryptUtils.sha1Digest(src.getBytes()));
	}

    
    public enum JceTransformation {
    	DES_ECB("DES/ECB/PKCS5Padding"),
    	DES_CBC("DES/CBC/PKCS5Padding"),
    	DES_ECB_NOPADDING("DES/ECB/NoPadding"),
    	DESEDE_CBC("DESede/CBC/PKCS5Padding"),
    	DESEDE_CBC_PKCS7("DESede/CBC/PKCS7Padding"),
    	DESEDE_ECB("DESede/ECB/PKCS5Padding");
		private JceTransformation(String transformation) {
			this.transformation = transformation;
		}
		private String transformation;
    	public String toString() {
    		return transformation;
    	};
    }
    
    //**3DES加密秘钥类型
    //LCK_HS_DES3 与恒生交互秘钥
    //LCK_HEXUN_DES3 与和讯大网交互秘钥类型
    public enum DES3_KEY_TYEP{
    	LCK_HS_DES3,LCK_HEXUN_DES3
    }
    
    public enum JceAlgorithm{
    	AES,DES,DESede,RSA,SHA1("SHA-1"),MD5;
		private JceAlgorithm() {
			this.algorithm = name();
		}
		private JceAlgorithm(String algorithm) {
			this.algorithm = algorithm;
		}
    	private String algorithm;
		public String toString() {
    		return algorithm;
    	}
    }
    
    public enum JceProvider{
    	SUN_PROVIDER("SunJCE"),BOUNCY_CASTLE_PROVIDER("BC");
		private JceProvider(String provider) {
			this.provider = provider;
		}
		private String provider;
    	public String toString(){
    		return provider;
    	}
    }
	
    public static void main(String[] args) throws Exception{
    	
    }
}
